﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace elsimulatorcsharp
{
    class Elevator
    {
        public enum ElevatorState
        {
            MOVING,
            DOOR_OPENING,
            DOOR_OPEN,
            DOOR_CLOSING,
            IDLE,
            Closed
        }

        public enum ElevatorDirection
        {
            UP,
            DOWN,
            NONE
        }

        private static short elevatorCount = 0;
        private short index;

        private ElevatorState state = ElevatorState.IDLE;
        private int doorOpen;

        //
        private short lastFloor;

        // People in the elevator
        private List<Person> people;
        private byte maximumCapacity;

        private bool lockToFloor;
        private ElevatorDirection directionAfterReachedLock;

        private short maxFloor;
        private short minFloor;
        private short position;
        private int posDecimal;
        private int numberOfIterationsItTakesForElevatorToReachNextFloor;
        private ElevatorDirection direction;

        private byte speed = 1;

        private bool[] floorList;
        private bool emergencyStop;

        /*
         * Elevator constructor
         */
        Elevator(short minF, short maxF, short fPos, byte maxCapacity, bool[] bl)
        {
            this.minFloor = minF;
            this.maxFloor = maxF;
            this.floorList = bl;
            doorOpen = 0;
            this.position = fPos;
            lastFloor = fPos;
            this.maximumCapacity = maxCapacity;
            direction = ElevatorDirection.NONE;

            people = new List<Person>();
            this.index = elevatorCount++;

            this.emergencyStop = false;

            numberOfIterationsItTakesForElevatorToReachNextFloor = 50;
        }

        public void ChangeDoorSize(sbyte deltaSize)
        {
            if (deltaSize > 0)
            {
                doorOpen += deltaSize;
                if (doorOpen >= 10)
                {
                    doorOpen = 10;
                    state = ElevatorState.DOOR_OPEN;
                }
            }
            else
            {
                doorOpen += deltaSize;
                if (doorOpen <= 0)
                {
                    if (!(people.Count == 0) || this.position != this.lastFloor)
                    {
                        SetFurthestAwayFloorToGoTo();
                        state = ElevatorState.MOVING;
                    }
                    else
                    {
                        state = ElevatorState.IDLE;
                    }
                    doorOpen = 0;

                }
            }
        }

        private void SetFurthestAwayFloorToGoTo() {
        short furthestAwayFloor = (short) position;
        if (this.direction == ElevatorDirection.DOWN) {
            foreach (Person person in people) {
                if (person.GetFloorDest() < furthestAwayFloor) {
                    furthestAwayFloor = (short) person.GetFloorDest();
                }
            }
        }
        else if (this.direction == ElevatorDirection.UP) {
            foreach (Person person in people) {
                if (person.GetFloorDest() > furthestAwayFloor) {
                    furthestAwayFloor = (short) person.GetFloorDest();
                }
            }
        } 
        lastFloor = furthestAwayFloor;
    }

        public void AddPerson(Person p)
        {
            people.Add(p);
        }

        public void RemovePerson(Person p)
        {
            people.Remove(p);
        }

        // Updates position for elevator
        public void ChangePosition()
        {
            switch (this.direction)
            {
                case ElevatorDirection.UP:
                    this.posDecimal += this.speed;
                    if (this.posDecimal % numberOfIterationsItTakesForElevatorToReachNextFloor == 0)
                    {
                        this.posDecimal = 0;
                        if (this.position < this.lastFloor)
                        {
                            this.position += 1;
                        }
                        else if (this.position > this.lastFloor)
                        {
                            this.position -= 1;
                        }
                        if ((this.IsDestFloor() || this.position == this.lastFloor))
                        {
                            this.state = ElevatorState.DOOR_OPENING;
                        }
                    }
                    break;
                case ElevatorDirection.DOWN:
                    this.posDecimal += this.speed;
                    if (this.posDecimal % numberOfIterationsItTakesForElevatorToReachNextFloor == 0)
                    {
                        this.posDecimal = 0;
                        if (this.position < this.lastFloor)
                        {
                            this.position += 1;
                        }
                        else if (this.position > this.lastFloor)
                        {
                            this.position -= 1;
                        }
                        if ((this.IsDestFloor() || this.position == this.lastFloor))
                        {
                            this.state = ElevatorState.DOOR_OPENING;
                        }
                    }
                    break;
            }
        }

        /*
         * If a person wants to stop here, it will return true,
         * otherwise it will return false.
         */
        bool IsDestFloor() {
        foreach (Person person in people) {
            if (person.GetFloorDest() == position) {
                return true;
            }
        }
        return false;
    }

        public bool IsNotFull()
        {
            if (maximumCapacity > this.people.Count)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        // Getters and setters
        int GetElevatorIdx()
        {
            return this.index;
        }

        public void SetState(ElevatorState state)
        {
            this.state = state;
        }

        public ElevatorState GetState()
        {
            return state;
        }

        void SetDoor(byte doorOpen)
        {
            this.doorOpen = doorOpen;
        }

        public double GetDoor()
        {
            double doorDouble = doorOpen / (double)10;
            return doorDouble;
        }

        public void SetDirection(ElevatorDirection d)
        {
            this.direction = d;
        }

        public ElevatorDirection GetDirection()
        {
            return direction;
        }

        void SetPosition(short position)
        {
            this.position = position;
        }

        public short GetPosition()
        {
            return position;
        }

        public double GetProperPosition()
        {
            double properPosition = position;
            if (this.position < this.lastFloor)
            {
                properPosition += (double)posDecimal / (double)numberOfIterationsItTakesForElevatorToReachNextFloor;
            }
            else if (this.position > this.lastFloor)
            {
                properPosition -= (double)posDecimal / (double)numberOfIterationsItTakesForElevatorToReachNextFloor;
            }

            /*switch (direction) {
                case UP:
                    if (this.position < this.lastFloor){
                        properPosition += (double)posDecimal/ (double)numberOfIterationsItTakesForElevatorToReachNextFloor;
                    } else if (this.position > this.lastFloor){
                        properPosition -= (double)posDecimal/ (double)numberOfIterationsItTakesForElevatorToReachNextFloor;
                    }
                    break;
                
                case DOWN:
                    if (this.position < this.lastFloor){
                        properPosition += (double)posDecimal/ (double)numberOfIterationsItTakesForElevatorToReachNextFloor;
                    } else if (this.position > this.lastFloor){
                        properPosition -= (double)posDecimal/ (double)numberOfIterationsItTakesForElevatorToReachNextFloor;
                    }
            }*/
            return properPosition;
        }

        public List<Person> GetPeople()
        {
            return people;
        }


        void SetMaxNumberOfPeople(byte maxNumber)
        {
            maximumCapacity = maxNumber;
        }

        byte GetMaxNumberOfPeople()
        {
            return maximumCapacity;
        }

        byte GetPeopleCount()
        {
            return (byte)this.people.Count;
        }

        bool GoesToFloor(int floorID)
        {
            return floorList[floorID];
        }

        public void SetLastFloor(short f)
        {
            this.lastFloor = f;
        }

        public short GetLastFloor()
        {
            return this.lastFloor;
        }

        /*  void setEmergencyStop(bool stop){
              if (stop){
                  this.emergencyStop = true;
              } else {
                  this.emergencyStop = false;
                  this.timer = new Date();
              }
          }*/
        public short GetIndex()
        {
            return this.index;
        }

        public bool IsDoorClosed()
        {
            bool doorClosed = false;
            if (this.doorOpen == 0)
            {
                doorClosed = true;
            }
            else
            {
                doorClosed = false;
            }

            return doorClosed;
        }


        public static void ResetCounter()
        {
            elevatorCount = 0;
        }
    }
}
